home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Utilities / RemoteCommand / Source / ExecMonitor.m < prev    next >
Encoding:
Text File  |  1993-06-11  |  14.6 KB  |  513 lines

  1. // -------------------------------------------------------------------------------------
  2. // ExecMonitor
  3. // -------------------------------------------------------------------------------------
  4. // Permission is granted to freely redistribute this source code, and to use fragments
  5. // of this code in your own applications if you find them to be useful.  This class,
  6. // along with the source code, come with no warranty of any kind, and the user assumes
  7. // all responsibility for its use.
  8. // -------------------------------------------------------------------------------------
  9.  
  10. #import <appkit/appkit.h>
  11. #import <libc.h>
  12. #import <stdlib.h>
  13. #import <c.h>
  14. #import <errno.h>
  15. #import <ctype.h>
  16. #import <math.h>
  17. #import <sys/param.h>
  18. #import <sys/types.h>
  19. #import <sys/time.h>
  20. #import <sys/wait.h>
  21. #import <sys/resource.h>
  22. #import "RemoteCommand.h"
  23. #import "userInfo.h"
  24. #import "ExecServer.h"
  25. #import "ExecScrollText.h"
  26. #import "ExecMonitor.h"
  27.  
  28. // -------------------------------------------------------------------------------------
  29. // misc defines
  30. #define    REMOTE_SERVER_CMD    "RemoteRunServer"
  31.  
  32. // -------------------------------------------------------------------------------------
  33. // misc defines
  34. #define    X                    origin.x
  35. #define    Y                    origin.y
  36. #define    W                    size.width
  37. #define    H                    size.height
  38. #define    freeString(X)        { if (X) { free((char*)X); X = 0; } }
  39. #define    freeCopy(T,F)        { freeString(T); if (F) T = NXCopyStringBuffer(F); }
  40. #define    cpnil                (char*)nil
  41. #define    exeBtnTITLE            "Execute\nCommand"
  42.  
  43. // -------------------------------------------------------------------------------------
  44. // ExecMonitor list of instances
  45. static id                    instanceList = (id)nil;
  46.  
  47. // -------------------------------------------------------------------------------------
  48. // list of cached user passwords
  49. typedef struct userPassword_s {
  50.     char                    *user;            // user name
  51.     char                    *password;        // password
  52.     void                    *next;            // pointer to next user
  53. } userPassword_t;
  54. static userPassword_t        *userList = (userPassword_t*)nil;
  55.  
  56. // -------------------------------------------------------------------------------------
  57. @implementation ExecMonitor
  58.  
  59. // -------------------------------------------------------------------------------------
  60. // user password
  61. // -------------------------------------------------------------------------------------
  62.  
  63. /* find user password */
  64. + (userPassword_t*)_findUserRcd:(const char*)user
  65. {
  66.     userPassword_t    *u;
  67.     for (u = userList; u && strcmp(user, u->user); u = (userPassword_t*)u->next);
  68.     return u;
  69. }
  70.  
  71. /* update user password */
  72. + (userPassword_t*)_addUser:(const char*)userName password:(const char*)password
  73. {
  74.     userPassword_t    *u;
  75.     if (!XUserVerifyPassword(userName, password)) return (userPassword_t*)nil;
  76.     u = (userPassword_t*)malloc(sizeof(userPassword_t));
  77.     u->user = NXCopyStringBuffer(userName);
  78.     u->password = NXCopyStringBuffer(password?password:"");
  79.     u->next = (void*)userList;
  80.     userList = u;
  81.     return u;
  82. }
  83.  
  84. /* find user password */
  85. + (const char*)findUserPassword:(const char*)user
  86. {
  87.     userPassword_t    *u = [self _findUserRcd:user];
  88.     return u? u->password : (char*)nil;
  89. }
  90.  
  91. /* update user password */
  92. + updateUser:(const char*)user password:(const char*)password
  93. {
  94.     userPassword_t    *u = [self _findUserRcd:user];
  95.     if (u) { if (strcmp(u->password, password)) freeCopy(u->password, password); }
  96.     else [self _addUser:user password:password];
  97.     return self;
  98. }
  99.  
  100. /* update user password */
  101. + removeUserPassword:(const char*)user
  102. {
  103.     userPassword_t    *u = userList, *l = (userPassword_t*)nil;
  104.     for (;u && strcmp(user,u->user); l = u, u = (userPassword_t*)u->next);
  105.     if (u) {
  106.         (l? l->next : userList) = u->next;
  107.         freeString(u->user);
  108.         freeString(u->password);
  109.         free(u);
  110.     }
  111.     return self;
  112. }
  113.  
  114. // -------------------------------------------------------------------------------------
  115. // object initialization
  116. // -------------------------------------------------------------------------------------
  117.  
  118. /* new exec monitor */
  119. + newExecHost:(const char*)host server:(const char*)server
  120. {
  121.     self = [[self alloc] init];
  122.     if (![self setRemoteHost:host server:server]) {
  123.         [self free];
  124.         return (id)nil;
  125.     }
  126.     [self showPanel:self];
  127.     return self;
  128. }
  129.  
  130. /* init */
  131. - init
  132. {
  133.     NXRect    wFrame;
  134.     
  135.     /* instanceList initialization */
  136.     if (!instanceList) instanceList = [[[List alloc] initCount:1] empty];
  137.     [instanceList addObject:self];
  138.     
  139.     /* init super */
  140.     [super init];
  141.     exeUser = (id)nil;
  142.     exePassword = (id)nil;
  143.     exeWindow = (id)nil;
  144.     exeCommandScroll = (id)nil;
  145.     
  146.     /* load nib */
  147.     if (![NXApp loadNibSection:"ExecMonitor.nib" owner:self]) {
  148.         NXLogError("[ExecMonitor] Could not load nib file 'ExecMonitor.nib'");
  149.         [NXApp delayedFree:self];
  150.         return self;
  151.     }
  152.     
  153.     /* spot-check nib loading */
  154.     if (!exeWindow || !exeCommandScroll) {
  155.         NXLogError("[ExecMonitor] 'ExecMonitor.nib' did not load properly");
  156.         [NXApp delayedFree:self];
  157.         return self;
  158.     }
  159.  
  160.     /* init vars */
  161.     saveFileName = (char*)nil;
  162.     useRunServer = YES;
  163.     cmdInProcess = NO;
  164.     shutDown     = NO;
  165.     exeRunServer = (id)nil;
  166.     
  167.     /* set user values if specified */
  168.     if (exeUser) [exeUser setStringValue:""];
  169.     if (exePassword) [exePassword setStringValue:""];
  170.     
  171.     /* init panel */
  172.     [exeExecute setTitle:exeBtnTITLE];
  173.     [exeCommandScroll clearScrollText];
  174.     [exeControlBox setBorderType:NX_NOBORDER];
  175.     [exeWindow setDelegate:self];
  176.     [exeWindow setFreeWhenClosed:NO];
  177.     [exeWindow getFrame:&wFrame];
  178.     minWinSize = wFrame.size;
  179.     
  180.     return self;
  181. }
  182.  
  183. /* free */
  184. - _free:sender { return [self free]; }
  185. - free
  186. {
  187.     shutDown = YES;
  188.     if (cmdInProcess) {    // loop until command has completed
  189.         [self exeExecute:(id)nil];
  190.         [self perform:@selector(_free:) with:self afterDelay:500 cancelPrevious:YES];
  191.         return (id)nil;
  192.     }
  193.     [exeWindow free];
  194.     [instanceList removeObject:self];    // make sure 'self' is removed
  195.     return [super free];
  196. }
  197.  
  198. // -------------------------------------------------------------------------------------
  199. // run arguments
  200.  
  201. /* set remote host (start server if necessary) */
  202. - setRemoteHost:(const char*)host server:(const char*)server
  203. {
  204.     char    cmd[MAXPATHLEN + 1], title[256];
  205.  
  206.     /* check for already set */
  207.     if (exeRunServer) {
  208.         NXLogError("[ExecMonitor] ExecServer is already running");
  209.         return (id)nil;
  210.     }
  211.     
  212.     /* instantiate ExecServer object */
  213.     exeRunServer = (host && *host)? [[ExecServer alloc] init] : (id)nil;
  214.     if (!exeRunServer) {
  215.         NXLogError("[ExecMonitor] Could not create ExecServer (no host specified)");
  216.         return (id)nil;
  217.     }
  218.     
  219.     /* set attributes */
  220.     [exeRunServer setMainAppPath:XAppPath()];
  221.     [exeRunServer setMainAppServerName:[NXApp appServerName] host:[NXApp appServerHost]];
  222.     [exeRunServer setRemoteHost:host];
  223.     if (server && *server) [exeRunServer setRemoteServerName:server];
  224.     sprintf(cmd, "%s/%s", XAppPath(), REMOTE_SERVER_CMD);
  225.     if (![exeRunServer setServerCommandName:cmd]) {
  226.         NXLogError("[ExecMonitor] Unable to set ExecServer command to %s", cmd);
  227.         exeRunServer = (id)nil;
  228.         return (id)nil;
  229.     }
  230.     
  231.     /* start server */
  232.     if (![exeRunServer startServer]) {
  233.         NXLogError("[ExecMonitor] Could not connect to RemoteRunServer");
  234.         exeRunServer = (id)nil;
  235.         return (id)nil;
  236.     }
  237.  
  238.     /* set window title */
  239.     sprintf(title, "ExecMonitor: %s (%s)",
  240.         [exeRunServer remoteHost], [exeRunServer remoteServerName]);
  241.     [exeWindow setTitle:title];
  242.     
  243.     return self;
  244. }
  245.  
  246. /* show ExecMonitor panel */
  247. - showPanel:sender
  248. {
  249.     return [exeWindow makeKeyAndOrderFront:(id)nil];
  250. }
  251.  
  252. // -------------------------------------------------------------------------------------
  253. // command execution
  254. // -------------------------------------------------------------------------------------
  255.  
  256. /* print messages */
  257. - _printCompletion:(BOOL)isError:(char*)fmt, ...
  258. {
  259.     char    *h = isError?">>>>>>>>>>":"----------", *t = isError?"<<<<<<<<<<":"----------";
  260.     va_list    args;
  261.     
  262.     /* message color and header */
  263.     if (isError) [exeShellScroll setTextAttributeColor:NX_COLORRED];
  264.     else [exeShellScroll setTextAttributeGray:NX_DKGRAY];
  265.     [exeShellScroll textPrintf:"\n%s ", h];
  266.     
  267.     /* message */
  268.     va_start(args, fmt);
  269.     [exeShellScroll textPrintf:fmt args:args];
  270.     va_end(args);
  271.     
  272.     /* message trailer and reset gray */
  273.     [exeShellScroll textPrintf:" %s\n\n", t];
  274.     [exeShellScroll setTextAttributeGray:textGray];
  275.     
  276.     /* return error condition */
  277.     return isError? (id)nil : self;
  278.  
  279. }
  280.  
  281. /* execute Server command */
  282. - exeExecute:sender
  283. {
  284.     int        commandLen;
  285.     char    *cmdStr, *user, *pass;
  286.  
  287.     /* check for no run server */
  288.     if (!exeRunServer) { NXBeep(); return (id)nil; }
  289.     
  290.     /* check for "STOP" */
  291.     if (cmdInProcess) {
  292.         if (!strcmp([exeExecute icon], "Stop")) {
  293.             if (useRunServer) [exeRunServer terminateCommand:execId];
  294.             else [exeShellScroll terminateCommand];
  295.             [exeExecute setIcon:"Kill"];
  296.         } else {
  297.             if (useRunServer) [exeRunServer killCommand:execId];
  298.             else [exeShellScroll killCommand];
  299.         }
  300.         return self;
  301.     }
  302.     if (!sender) return (id)nil;
  303.  
  304.     /* exit if shutdown in progress */
  305.     if (shutDown) return (id)nil;
  306.     
  307.     /* get user */
  308.     user = exeUser? (char*)[exeUser stringValue] : (char*)nil;
  309.     if (!user || !*user) user = (char*)[NXApp appUserName];
  310.     
  311.     /* get password (if needed) */
  312.     if (!XIsCurrentUser(user)) {
  313.         pass = exePassword? (char*)[exePassword stringValue] : "";
  314.         if ([exeRunServer needUserPassword:user] && !XUserVerifyPassword(user, pass)) {
  315.             [[self class] removeUserPassword:user];
  316.             return [self _printCompletion:1:"User password not valid"];
  317.         }
  318.         [[self class] updateUser:user password:pass];
  319.     }
  320.  
  321.     /* setup command */
  322.     commandLen = [[exeCommandScroll docView] textLength] + 1;
  323.     cmdStr = (char*)malloc(commandLen + 1);
  324.     [[exeCommandScroll docView] getSubstring:cmdStr start:0 length:commandLen];
  325.     strcat(cmdStr, "\n");
  326.     
  327.     /* print command info */
  328.     [exeShellScroll setTextAttributeGray:NX_DKGRAY];
  329.     [exeShellScroll textPrintf:"\n---------- Executing Command ----------\n"];
  330.     [exeShellScroll textPrintf:"%s", cmdStr];
  331.     [exeShellScroll textPrintf:  "---------------------------------------\n\n"];
  332.     [exeShellScroll setTextAttributeGray:textGray];
  333.     
  334.     /* run command */
  335.     if (useRunServer) {
  336.         execId = [exeRunServer runCommand:cmdStr
  337.                     withUser:user:[[self class] findUserPassword:user]
  338.                     forClient:exeShellScroll
  339.                     killOnError:YES];
  340.     } else {
  341.         execId = (execHandle_t)[exeShellScroll runCommand:cmdStr];
  342.     }
  343.     
  344.     /* free command */
  345.     free(cmdStr);
  346.  
  347.     /* check to see if command actually started running */
  348.     if (!execId) return [self _printCompletion:1:"Command failed to start"];
  349.  
  350.     /* change button */
  351.     cmdInProcess = YES;
  352.     [exeExecute setIconPosition:NX_ICONONLY];
  353.     [exeExecute setIcon:"Stop"];
  354.     
  355.     return self;
  356. }
  357.  
  358. /* call-back from shell command (main thread) */
  359. - commandDidComplete:shellId withError:(int)theError
  360. {
  361.  
  362.     /* check for completion status */
  363.     if (!theError) {    // successful
  364.         [self _printCompletion:0:"completed normally"];
  365.     } else {
  366.         char *desc = [ExecServer errorDesc:theError];
  367.         if (desc) [self _printCompletion:1:"%s",desc];
  368.         else [self _printCompletion:1:"Terminated with exit(%d)",theError];
  369.     }
  370.  
  371.     /* remove user if password is bad */
  372.     if ((theError == RSRV_BADPASSWD) && exeUser) {
  373.         [ExecMonitor removeUserPassword:[exeUser stringValue]];
  374.     }
  375.  
  376.     /* reset execution button */
  377.     [exeExecute setEnabled:YES];
  378.     [exeExecute setIconPosition:NX_TITLEONLY];
  379.     [exeExecute setTitle:exeBtnTITLE];
  380.     cmdInProcess = NO;
  381.     execId = (execHandle_t)nil;
  382.     
  383.     return self;
  384. }
  385.  
  386. // -------------------------------------------------------------------------------------
  387. // Terminal invoke
  388. // -------------------------------------------------------------------------------------
  389.  
  390. /* invoke command in Terminal window */
  391. + terminalCommand:(const char*)cmd title:(const char*)title
  392. {
  393.     Speaker    *speaker;
  394.     port_t    terminalPort;
  395.     
  396.     /* can't find Terminal */
  397.     if (!(terminalPort = NXPortFromName("Terminal", NULL))) return (id)nil;
  398.     
  399.     /* launch Terminal */
  400.     [NXApp deactivateSelf];
  401.     creat("/tmp/.reallyignorethis.term", 0444);
  402.     [[Application workspace] openFile:"/tmp/.reallyignorethis.term"
  403.         fromImage:(id)nil at:(NXPoint*)nil inView:(id)nil];
  404.     
  405.     /* run command */
  406.     speaker = [NXApp appSpeaker];
  407.     [speaker setSendPort: terminalPort];
  408.     [speaker selectorRPC:"runCommand:usingShell:inFolder:windowTitle:closeOnExit:"
  409.         paramTypes: "cccci", cmd, "", "", title, NO];
  410.  
  411.     return self;
  412.     
  413. }
  414.  
  415. // -------------------------------------------------------------------------------------
  416. // outlets
  417. // -------------------------------------------------------------------------------------
  418.  
  419. - setExeShellScroll:anObject
  420. {
  421.     exeShellScroll = [[ExecScrollText newExecScrollText:anObject] clearScrollText];
  422.     [exeShellScroll setDelegate:self];
  423.     textFont = [[exeShellScroll docView] font];
  424.     textGray = [[exeShellScroll docView] textGray];
  425.     [exeShellScroll setTab:[textFont getWidthOf:"        "] count:10];
  426.     [exeShellScroll textPrintf:"\n"];    // this forces the scroll view to initialize
  427.     [exeShellScroll clearScrollText];
  428.     return self;
  429. }
  430.  
  431. - setExeCommandScroll:anObject
  432. {
  433.     exeCommandScroll = [[ExecScrollText newExecScrollText:anObject] clearScrollText];
  434.     return self;
  435. }
  436.  
  437. // -------------------------------------------------------------------------------------
  438. // first responder methods
  439. // -------------------------------------------------------------------------------------
  440.  
  441. /* save error handler */
  442. - (BOOL)attemptOverwrite:(const char*)fileName
  443. {
  444.     NXLogError("[ExecMonitor] Unable to save %s", fileName);
  445.     return NO;
  446. }
  447.  
  448. /* save contents of text view */
  449. - saveAs:sender
  450. {
  451.     char    *temp;
  452.     id        savePanel = [SavePanel new];
  453.  
  454.     /* set up save panel */
  455.     [savePanel setTitle:"Save Command Output"];
  456.     [savePanel setPrompt:"File:"];
  457.     [savePanel setRequiredFileType:""];
  458.     [savePanel setDirectory:(saveFileName?saveFileName:[NXApp appUserHome])];
  459.  
  460.     /* get file name to save */
  461.     if (![savePanel runModalForDirectory:[NXApp appUserHome] file:""]) return self;
  462.     if (!(temp = (char*)[savePanel filename])) return self;
  463.     freeString(saveFileName);
  464.     saveFileName = NXCopyStringBuffer(temp);
  465.     
  466.     return [self save:sender];
  467. }
  468.  
  469. /* save contents of text view */
  470. - save:sender
  471. {
  472.     if (!saveFileName) return [self saveAs:sender];
  473.     [[exeShellScroll docView] saveRTFDTo:saveFileName removeBackup:NO errorHandler:self];
  474.     return self;
  475. }
  476.  
  477. // -------------------------------------------------------------------------------------
  478. // window close
  479. // -------------------------------------------------------------------------------------
  480.  
  481. /* close all windows */
  482. + closeAllWindows
  483. {
  484.     if (instanceList) [instanceList makeObjectsPerform:@selector(closeWindow:) with:self];
  485.     return self;
  486. }
  487.  
  488. /* shut down */
  489. - closeWindow:sender
  490. {
  491.     return [exeWindow performClose:self];
  492. }
  493.  
  494. // -------------------------------------------------------------------------------------
  495. // window delegate methods
  496. // -------------------------------------------------------------------------------------
  497.  
  498. - windowWillClose:windowId
  499. {
  500.     if (cmdInProcess) return (id)nil;
  501.     [instanceList removeObject:self];
  502.     return [NXApp delayedFree:self];
  503. }
  504.  
  505. - windowWillResize:windowId toSize:(NXSize*)newSize
  506. {
  507.     if (newSize->width  < minWinSize.width ) newSize->width  = minWinSize.width;
  508.     if (newSize->height < minWinSize.height) newSize->height = minWinSize.height;    
  509.     return self;
  510. }
  511.  
  512. @end
  513.